from xml.dom.minidom import parseString,parse
from xml.dom import Node
from obj_types.obj_image import Image
from obj_types.obj_text import Text
import os

class Presentation:
    __doc__=""" 
A class to hold presentation data - you shoudn't need more than one
    
Fields:
    slides - a lsit containing all the slides, in DOM tree format
           
Constants:
    None yet.

Methods:
    __init__(presentation) - constructor, accepts 1 paramater
    get_slide(number) - returns a list containing objects needed for displaying given slide
    get_number_of_slides() - pretty self-explanatory
    
Future improvements:
    Possibly, a method which returns a PyGTK paintable object.
    If it were Java, it would have proabably been called paint(Graphics g)
""" 
    
    def __init__(self,presentation,isfile,tcp_client):
        #presentation - string containing xml data
        self.is_ok=True                
        self.slides=[]

        try:
            self.client=tcp_client
            if isfile:
                try:
                    document=parse(presentation)
                except IOError:
                    self.is_ok=False                
                    return
            else:
                document=parseString(presentation)

            #adds all the slides in the list
            #NOTE: if we plan to use it, we chould also parse the slide number
            #    currently, this is ignored
            for slide in document.getElementsByTagName('presentation').item(0).childNodes:
                if slide.nodeType==Node.ELEMENT_NODE:
                    self.slides.append(slide)
            del document    #we don't need the DOM tree any more 
        except:
            self.is_ok=False
    
    def get_slide(self,nr):
        if nr<len(self.slides) and nr>=0:
            slide=self.slides[nr]    #the slide being parsed
            rez=[]    #where the results will be held (and returned)
            defaultFont = Text()    #the default font for the current slide
            
            #parsing 'bgcolor' attribute
            if slide.hasAttribute('bgcolor'):
                rez.append(slide.getAttributeNode('bgcolor').nodeValue)
            else:
                rez.append('white')
            
            #parsing 'textcolor' attribute
            if slide.hasAttribute('textcolor'):
                defaultFont.set_attrib('color',slide.getAttributeNode('textcolor').nodeValue)
            
            #parsing 'textsize' attribute
            if slide.hasAttribute('textsize'):
                try:
                    defaultFont.set_attrib('size',int(slide.getAttributeNode('textsize').nodeValue))
                except:	#size is not numerical
                    pass
            
            #parsing 'font' attribute
            if slide.hasAttribute('font'):
                defaultFont.set_attrib('font',slide.getAttributeNode('font').nodeValue)
            
            int_imageno = 0
            for node in slide.childNodes:
                #for each of its children, we parse the content
                if node.nodeType != Node.ELEMENT_NODE:
                    #we need to ignore if the user inputs anything else (i.e. does manual indentation)
                    continue
#                if node.nodeName == 'title':
#                    rez.append(node.firstChild.nodeValue)
                if node.nodeName == 'img':
                    #an image node
                    filename="img"+str(nr)+"_"+str(int_imageno)+".png"
                    int_imageno+=1
                    rez.append(get_image_attributes(node,self.client,filename))
                elif node.nodeName == 'par' or node.nodeName == 'title':
                    #essentially, 'par' and 'title' nodes are identical in content
                    rez.append(parse_paragraph_node(node,defaultFont))
                else:
                    #everything else ignored
                    continue
            return rez
        elif nr<0:
            return ['black',[Text('Loading...',bold=True,size=20,color='white')]]
        else:
            #there is no slide with the given number
            #currently, this returns None (null, whatever)
            #it could return a valuea meaning "end of show" or "error", signaling the application to quit
            return ['black',[]]
    
    def get_number_of_slides(self):
        return len(self.slides)
    
#Parses the image atributes that can be extracted from the current node
def get_image_attributes(node, downloader, filename):
    rez=[]
    #we need to have at least source and dimensions
    if (not node.hasAttribute('src')) or (not node.hasAttribute('height')) or (not node.hasAttribute('width')):
        print 'Incompletely defined image'
        #The displaying process should know how to handle 'None'
        return None
    try:
        image = Image(node.getAttributeNode('src').nodeValue,
                         int(node.getAttributeNode('width').nodeValue),
                         int(node.getAttributeNode('height').nodeValue))
        if node.hasAttribute('align'):
            #this could be easier done by calling something like
            #image.setAlign(node.getAttributeNode('align').nodeValue)
            #and do the parsing in objTypes.Image
            if node.getAttributeNode('align').nodeValue == 'center':
                image.align=Image.ALIGN_CENTER
            elif node.getAttributeNode('align').nodeValue == 'left':
                image.align=Image.ALIGN_LEFT
            elif node.getAttributeNode('align').nodeValue == 'right':
                image.align=Image.ALIGN_RIGHT
            else:
                pass
        if (image.src)[0:4] == "http":
            if downloader is not None:
                str_file = downloader.get_image(filename,image.src)
                if str_file is None: 
                    return None
                else:
                    image.src=str_file
            else:
                return None
        #WARNING - the image should reside on the host computer, else error!
        return image
    except ValueError:
        #This happens if the values are incorrect - see error output:
        print 'either width or height incorrectly specified'
        return None

#Parses text attributes that can be extracted from the current node
#Recursively, it adds chunks to a list, each chunk being an objText
def parse_text(node,font,chunks):
    if node.nodeType==Node.TEXT_NODE:
        #the node is actually a bunch of text
        x=Text(node.nodeValue)
        x.copy_style(font)    #the text will have the same font as its parent node
        chunks.append(x)
    else:
        newFont = Text()
        #if the node is not a text node, it means there are some necessarry changes to the style
        #the new style should be the same as the old one, except for the one different aspect
        #(only one attribute can be changed by the recursion) 
        newFont.copy_style(font)
        if node.nodeName=='s' or node.nodeName=='b' or node.nodeName=='u' or node.nodeName=='i' or node.nodeName=='sub' or node.nodeName=='sup' or node.nodeName=='h1' or node.nodeName=='h2' or node.nodeName=='h3':
            newFont.set_attrib(node.nodeName)
        #a small problem - if the user declares a h1 node inside a h1 node, the effects will be applied twice.
        #this shouldn't happen for a properly designed presentation
        if node.nodeName=='style':
            if node.hasAttribute('font'):
            	newFont.set_attrib('font', node.getAttributeNode('font').nodeValue)
            if node.hasAttribute('size'):
            	newFont.set_attrib('size', node.getAttributeNode('size').nodeValue)
            if node.hasAttribute('color'):
            	newFont.set_attrib('color', node.getAttributeNode('color').nodeValue)

#            newFont.set_attrib(node.nodeName, node.getAttributeNode('value').nodeValue)
        for child in node.childNodes:
            #the children are parsed with the new style
            parse_text(child,newFont,chunks)

#Returns the list with text chunks for the given paragraph node
def parse_paragraph_node(parent, font):
	chunks=[]    #this will be returned
	paragraph_style = Text()
	paragraph_style.copy_style(font)
	if parent.hasAttribute('font'):
		paragraph_style.set_attrib('font', parent.getAttributeNode('font').nodeValue)
	if parent.hasAttribute('size'):
		paragraph_style.set_attrib('size', parent.getAttributeNode('size').nodeValue)
	if parent.hasAttribute('color'):
		paragraph_style.set_attrib('color', parent.getAttributeNode('color').nodeValue)
	for node in parent.childNodes:
		parse_text(node,paragraph_style,chunks)
	return chunks
